import copy
import re

from my_typing import *
from utils.serializer_utils.exceptions import (
    BuiltinSignatureError,
    DjangoValidationError,
    ErrorDetail,
    ObjectDoesNotExist,
    SkipField,
    ValidationError,
    is_simple_callable,
)
from utils.serializer_utils.utils import representation


class empty:  # noqa
    """
    该类用于表示没有为给定的输入或输出值提供数据
    它是必需的，因为 `None` 可能是一个有效的输入或输出值
    """


def get_attribute(instance, attrs: ListStr):
    """
    获取对象的属性

        - 与 Python 内置的 `getattr(instance, attr)` 类似，但采用嵌套属性列表，而不是单个属性
        - 还接受对象的属性查找或字典查找

    Args:
        instance: 对象实例
        attrs: 属性列表

    Returns:
        属性值
    """

    for attr in attrs:
        try:
            if isinstance(instance, Mapping):
                instance = instance[attr]

            else:
                instance = getattr(instance, attr)

        except ObjectDoesNotExist:
            return None

        if is_simple_callable(instance):
            try:
                instance = instance()

            except (AttributeError, KeyError) as exc:
                # 如果我们在这里引发 `Attribute` 或 `KeyError`，它将被视为 `Field.get_attribute()` 中的省略字段
                # 相反，我们引发 `ValueError` 以确保异常未被屏蔽
                raise ValueError(f'Exception raised in callable attribute "{attr}"; original exception was: {exc}')

    return instance


def set_value(
    dictionary: dict,
    keys: ListStr,
    value,
) -> None:
    """
    与 Python 内置的 `dictionary[key] = value` 类似，但采用嵌套键列表而不是单个 key

    set_value({"a": 1}, [], {"b": 2}) -> {"a": 1, "b": 2}
    set_value({"a": 1}, ["x"], 2) -> {"a": 1, "x": 2}
    set_value({"a": 1}, ["x", "y"], 2) -> {"a": 1, "x": {"y": 2}}
    """

    if not keys:
        dictionary.update(value)
        return

    for key in keys[:-1]:
        if key not in dictionary:
            dictionary[key] = {}

        dictionary = dictionary[key]

    dictionary[keys[-1]] = value


def to_choices_dict(choices):
    """
    Convert choices into key/value dicts.

    to_choices_dict([1]) -> {1: 1}
    to_choices_dict([(1, "1st"), (2, "2nd")]) -> {1: "1st", 2: "2nd"}
    to_choices_dict([("Group", ((1, "1st"), 2))]) -> {"Group": {1: "1st", 2: "2"}}
    """

    # Allow single, paired or grouped choices style:
    # choices = [1, 2, 3]
    # choices = [(1, "First"), (2, "Second"), (3, "Third")]
    # choices = [("Category", ((1, "First"), (2, "Second"))), (3, "Third")]
    ret = OrderedDict()  # noqa

    for choice in choices:
        if not isinstance(choice, (list, tuple)):
            # single choice
            ret[choice] = choice

        else:
            key, value = choice
            if isinstance(value, (list, tuple)):
                # grouped choices (category, sub choices)
                ret[key] = to_choices_dict(value)

            else:
                # paired choice (key, display value)
                ret[key] = value
    return ret


def flatten_choices_dict(choices):
    """
    Convert a group choices dict into a flat dict of choices.

    flatten_choices_dict({1: "1st", 2: "2nd"}) -> {1: "1st", 2: "2nd"}
    flatten_choices_dict({"Group": {1: "1st", 2: "2nd"}}) -> {1: "1st", 2: "2nd"}
    """

    ret = OrderedDict()  # noqa

    for key, value in choices.items():
        if isinstance(value, dict):
            # grouped choices (category, sub choices)
            for sub_key, sub_value in value.items():
                ret[sub_key] = sub_value

        else:
            # choice (key, display value)
            ret[key] = value

    return ret


def iter_options(grouped_choices, cutoff=None, cutoff_text=None):
    """ Helper function for options and option groups in templates. """

    class StartOptionGroup:
        start_option_group = True
        end_option_group = False

        def __init__(self, label):
            self.label = label

    class EndOptionGroup:
        start_option_group = False
        end_option_group = True

    class Option:
        start_option_group = False
        end_option_group = False

        def __init__(
            self,
            value,
            display_text,
            disabled: bool = False,
        ) -> None:
            self.value = value
            self.display_text = display_text
            self.disabled = disabled

    count = 0

    for key, value in grouped_choices.items():
        if (
            cutoff
            and count >= cutoff
        ):
            break

        if isinstance(value, dict):
            yield StartOptionGroup(label=key)

            for sub_key, sub_value in value.items():
                if (
                    cutoff
                    and count >= cutoff
                ):
                    break

                yield Option(value=sub_key, display_text=sub_value)
                count += 1

            yield EndOptionGroup()

        else:
            yield Option(value=key, display_text=value)
            count += 1

    if (
        cutoff
        and count >= cutoff
        and cutoff_text
    ):
        cutoff_text = cutoff_text.format(count=cutoff)
        yield Option(value="n/a", display_text=cutoff_text, disabled=True)


def get_error_detail(exc_info):
    """
    给定 Django ValidationError，返回 ErrorDetail 列表，并填充 "code"
    """

    code = getattr(exc_info, "code", None) or "invalid"

    try:
        error_dict = exc_info.error_dict

    except AttributeError:
        return [
            ErrorDetail(
                (
                    (error.message % error.params)
                    if error.params
                    else error.message
                ),
                code=(
                    error.code
                    if error.code
                    else code
                ),
            )
            for error in exc_info.error_list
        ]

    return {
        k: [
            ErrorDetail(
                (
                    (error.message % error.params)
                    if error.params
                    else error.message
                ),
                code=(
                    error.code
                    if error.code
                    else code
                ),
            )
            for error in errors
        ] for k, errors in error_dict.items()
    }


REGEX_TYPE = type(re.compile(""))
""" 正则表达式类型 """

MISSING_ERROR_MESSAGE = "由 `{class_name}` 引发的 ValidationError，但错误的 key `{key}` 在 `error_messages` 字典中不存在"
""" 缺失的错误消息 """


class Field:
    """ 字段 """

    _creation_counter: int = 0
    """
    创建顺序

    在序列化器中保持字段的顺序，每当创建一个 Field 类的实例时，就会将 _creation_counter 属性 +1
    """

    default_error_messages: DictStrStr = {
        "required": "请填写该字段",
        "null": "该字段不能为空",
    }
    """ 默认的错误消息 """

    default_error_messages_with_label: DictStrStr = {
        "required": "请填写{label}",
        "null": "{label}不能为空",
    }
    """ 默认的错误消息（带字段名） """

    default_validators: list = []
    """ 默认的验证器 """

    initial = None
    """
    字段的初始值

    序列化器被初始化时所使用的默认值，通常用于在反序列化时给出默认值或在序列化时提供一些默认信息
    """

    def __init__(
        self,
        label: str = None,
        required: bool = True,
        default: Any = empty,
        allow_null: bool = False,
        show_label: bool = True,
    ) -> None:
        """
        Args:
            label: 字段标签
            required: 是否必填
            default: 默认值
            allow_null: 是否允许为空
            show_label: 是否要在错误提示中显示字段名称
        """

        self.initial = self.initial
        """ 最初的值 """

        self.label: str = label
        """ 字段标签 """

        self.required: bool = required
        """ 是否必填 """

        self.default = default
        """ 默认值 """

        self.allow_null: bool = allow_null
        """ 是否允许为空 """

        self.show_label: bool = show_label
        """ 是否要在错误提示中显示字段名称 """

        # 当字段添加到序列化器时，这些是由 `.bind()` 设置的
        self.field_name = None
        """ 字段名 """

        self.parent = None
        """ 父序列化器实例 """

        error_messages_map: str = (
            "default_error_messages_with_label"
            if self.show_label
            else "default_error_messages"
        )
        """ 错误消息映射 """

        self.error_messages: DictStrStr = {
            k: v
            for cls in reversed(self.__class__.__mro__)  # 循环1：遍历父类（顶级父类 -> 当前类）
            for k, v in getattr(cls, error_messages_map, {}).items()  # 循环2：遍历父类的默认的错误消息
        }
        """ 从 self 和父类收集默认的错误消息 """

        self.source = None

    def bind(
        self,
        field_name: str,
        parent: "Serializer",
    ) -> None:
        """
        初始化字段实例的字段名称和父级

        当字段添加到父序列化程序实例时调用

        Args:
            field_name: 字段名
            parent: 父序列化器实例
        """

        self.field_name: str = field_name
        """ 字段名 """

        self.parent = parent
        """ 父序列化器实例 """

        # 如果没有填写 label，则 label 为：首字母大写的字段名
        if self.label is None:
            self.label = field_name.replace("_", " ").capitalize()

        self.source_attrs: ListStr = field_name.split(".")  # noqa
        """ 序列化实例或填充验证数据时需要查找的属性列表 """

    @property
    def validators(self) -> list:
        """
        验证器

        是一个延迟加载的属性，它从 `get_validators` 获取默认值
        """

        if not hasattr(self, "_validators"):
            self._validators = self.get_validators()  # noqa

        return self._validators

    @validators.setter
    def validators(self, validators) -> None:
        """ 验证器 Setter """

        self._validators = validators  # noqa

    def get_validators(self) -> list:
        """ 获取验证器 """

        return list(self.default_validators)

    def get_initial(self):
        """ 当字段作为原始值返回且没有任何对象实例时，返回要使用的值 """

        if callable(self.initial):
            return self.initial()

        return self.initial

    def get_value(self, dictionary):
        """ 给定传入的原始数据，返回应验证并转换为本机值的该字段的值 """

        return dictionary.get(self.field_name, empty)

    def get_attribute(self, instance):
        """ 给定传出对象实例，返回应用于该字段的原始值 """

        try:
            return get_attribute(instance, self.source_attrs)

        except BuiltinSignatureError as exc:
            msg = (
                "Field source for `{serializer}.{field}` maps to a built-in "
                "function type and is invalid. Define a property or method on "
                "the `{instance}` instance that wraps the call to the built-in "
                "function.".format(
                    serializer=self.parent.__class__.__name__,
                    field=self.field_name,
                    instance=instance.__class__.__name__,
                )
            )
            raise type(exc)(msg)

        except (KeyError, AttributeError) as exc:
            if self.default is not empty:
                return self.get_default()

            if self.allow_null:
                return None

            if not self.required:
                raise SkipField()

            msg = (
                "Got {exc_type} when attempting to get a value for field "
                "`{field}` on serializer `{serializer}`.\nThe serializer "
                "field might be named incorrectly and not match "
                "any attribute or key on the `{instance}` instance.\n"
                "Original exception text was: {exc}.".format(
                    exc_type=type(exc).__name__,
                    field=self.field_name,
                    serializer=self.parent.__class__.__name__,
                    instance=instance.__class__.__name__,
                    exc=exc
                )
            )

            raise type(exc)(msg)

    def get_default(self):
        """
        如果没有为此字段提供输入，则返回验证数据时要使用的默认值

        如果尚未为此字段设置默认值，则只会引发 `SkipField` ，表明不应在该字段的验证数据中设置任何值
        """

        if (
            self.default is empty
            or getattr(self.root, "partial", False)
        ):
            # No default, or this is a partial update.
            raise SkipField()

        if callable(self.default):
            return self.default()

        return self.default

    def validate_empty_values(self, data) -> tuple[bool, AnyOrNone]:
        """
        验证空值，并且

        - 引发 `ValidationError`，指示无效数据。
        - 引发 `SkipField`，表明该字段应该被忽略
        - Return (True, data)，指示应返回一个空值，而不应用任何进一步的验证。
        - Return (False, data)，指示非空值，应正常应用验证。

        Args:
            data: 传入的数据

        Returns:
            是否为空值, 要返回的数据
        """

        if data is empty:
            if self.required:
                self.fail("required")

            return True, self.get_default()

        if data is None:
            if not self.allow_null:
                self.fail("null")

            return True, None

        return False, data

    def run_validation(self, data=empty):
        """
        验证一个简单的表示（常指传输层和应用层之间的数据表现形式，比如 JSON、XML 等），并返回内部值

        如果输入中未包含表示，则提供的数据可能为空

        如果该字段不应包含在验证过的数据中，则可能引发 `SkipField` 异常
        这通常用于自定义验证逻辑中，以排除特定字段的验证
        """

        is_empty_value, data = self.validate_empty_values(data)

        if is_empty_value:
            return data

        value = self.to_internal_value(data)
        self.run_validators(value)

        return value

    def run_validators(self, value):
        """ 针对字段上的所有验证器测试给定值，并引发 `ValidationError` 或简单地返回 """

        errors = []

        for validator in self.validators:
            try:
                validator(value)

            except ValidationError as exc:
                # If the validation error contains a mapping of fields to
                # errors then simply raise it immediately rather than
                # attempting to accumulate a list of errors.
                if isinstance(exc.detail, dict):
                    raise

                errors.extend(exc.detail)

            except DjangoValidationError as exc:
                errors.extend(get_error_detail(exc))

        if errors:
            raise ValidationError(errors)

    def to_internal_value(self, data):
        """ 将传入的原始数据转换为本机值 """

        raise NotImplementedError(
            "{cls}.to_internal_value() must be implemented for field "
            "{field_name}. If you do not need to support write operations "
            "you probably want to subclass `ReadOnlyField` instead.".format(
                cls=self.__class__.__name__,
                field_name=self.field_name,
            )
        )

    def to_representation(self, value):
        """ 将传出的本机值转换为原始数据 """

        raise NotImplementedError(
            "{cls}.to_representation() must be implemented for field {field_name}.".format(
                cls=self.__class__.__name__,
                field_name=self.field_name,
            )
        )

    def fail(self, key: str, **kwargs) -> None:
        """ 只是引发验证错误的辅助方法 """

        try:
            msg = self.error_messages[key]

        except KeyError:
            class_name = self.__class__.__name__
            msg = MISSING_ERROR_MESSAGE.format(class_name=class_name, key=key)

            raise AssertionError(msg)

        if (
            self.show_label
            and "{label}" in msg
        ):
            kwargs["label"] = self.label

        message_string = msg.format(**kwargs)

        raise ValidationError(message_string)

    @property
    def root(self) -> "Field":
        """ 返回该字段的顶级序列化器 """

        root = self

        while root.parent is not None:
            root = root.parent

        return root

    @property
    def context(self) -> dict:
        """ 返回初始化时传递给根序列化器的上下文 """

        return getattr(self.root, "_context", {})

    def __new__(cls, *args, **kwargs) -> "Field":
        """
        当一个字段被实例化时，我们存储所使用的参数，以便我们可以提供对象的有用表示
        """

        instance = super().__new__(cls)
        instance._args = args
        instance._kwargs = kwargs

        return instance

    def __deepcopy__(self, memo):
        """ 当克隆字段时，我们使用最初创建它的参数来实例化，而不是复制完整的状态 """

        # Treat regexes and validators as immutable.
        # See https://github.com/encode/django-rest-framework/issues/1954
        # and https://github.com/encode/django-rest-framework/pull/4489
        args = [
            (
                copy.deepcopy(item)
                if not isinstance(item, REGEX_TYPE)
                else item
            )
            for item in self._args
        ]
        kwargs = {
            key: (
                copy.deepcopy(value, memo)
                if key not in ("validators", "regex")
                else value
            )
            for key, value in self._kwargs.items()
        }

        return self.__class__(*args, **kwargs)

    def __repr__(self):
        """
        字段使用其初始调用参数来表示
        这允许我们为序列化器实例创建描述性表示，以显示序列化器上所有声明的字段
        """

        return representation.field_repr(self)
